#include <memory>
#include <random>
#define DRAM_SIZE 8*1024
#define SRAM_SIZE 8*1024
#include "type.h"
#include "util.h"
#include "device/conv_dma.h"
#include "mem/dram.h"
#include "mem/sram.h"
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "jsmn.h"

void dump_tofile(std::string file_name, u8* dump_data,
              u8* is_valid, u32 row_len, u32 col_len){
    FILE *fp;
    fp = fopen(file_name.c_str(),"w");
    //std::ofstream ofile_stream(file_name);
    if(is_valid!=NULL){
        for(u32 index_i = 0;index_i < col_len;index_i++){
            for(u32 index_j = 0;index_j < row_len;index_j++){
                if(is_valid[index_i * row_len + index_j])
                    //ofile_stream << std::hex << (u32)(u8)dump_data[index_i * row_len + index_j];
                    fprintf(fp,"%02x",dump_data[index_i * row_len + index_j]);
                else
                    //ofile_stream << "xx";
                    fprintf(fp,"xx");
            }
            //ofile_stream << std::endl;
            fprintf(fp, "\n");
        }
    }
    else{
        for(u32 index_i = 0;index_i < col_len;index_i++){
            for(u32 index_j = 0;index_j < row_len;index_j++){
                fprintf(fp,"%02x",dump_data[index_i * row_len + index_j]);
                //printf("%02x",dump_data[index_i * row_len + index_j]);
                //ofile_stream << std::hex << (u32)(u8)dump_data[index_i * row_len + index_j];
            }
            //ofile_stream << std::endl;
            fprintf(fp, "\n");
            //printf("\n");
        }
    }
    fclose(fp);
}

void dump_fromfile(std::string file_name, u8* dump_data,
                   u32 row_len, u32 col_len){
    FILE *fp;
    fp = fopen(file_name.c_str(),"r");
    //std::ofstream ofile_stream(file_name);
    for(u32 index_i = 0;index_i < col_len;index_i++){
         for(u32 index_j = 0;index_j < row_len;index_j++){
             fscanf(fp,"%02x",&dump_data[index_i * row_len + index_j]);
             //printf("%02x",dump_data[index_i * row_len + index_j]);
             //ofile_stream << std::hex << (u32)(u8)dump_data[index_i * row_len + index_j];
         }
         //ofile_stream << std::endl;
         fscanf(fp, "\n");
         //printf("\n");
    }
    fclose(fp);
}

static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
	if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
			strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
		return 0;
	}
	return -1;
}

int getvalue(const char *json, int start,int end)
{
    int ret = 0;
    for(int i =start;i<end;i++){
        char t = json[i];
        if(t <= '9' && t >= '0')
            ret = ret * 16 + t - '0';
        else if(t <='f' && t >= 'a')
            ret = ret * 16 + t - 'a' + 10;
        else if(t <='F' && t >= 'A')
            ret = ret * 16 + t - 'A' + 10;
        else
            printf("error value:%c\n", t);
    }
    return ret;
}

int main(int argc, char** argv){
    if(argc!=2){
        printf("usage ./dma_test.bin json_file\n");
        return -1;
    }
    int i;
    int r;
    jsmn_parser p;
    jsmntok_t* t;
    jsmn_init(&p);

    t = new jsmntok_t[4096];

    FILE* fp=fopen(argv[1], "r");
    if(fp == NULL)
    {
        printf("ERROR:cannot read file %s!\n",argv[1]);
        return -1;
    }
    fseek(fp, 0, SEEK_END);
    long fsize =ftell(fp);
    rewind(fp);

    char *json_buf = new char[fsize * 2];
    fread(json_buf, sizeof(char), fsize, fp);

    r = jsmn_parse(&p, json_buf, fsize, t, 4096);
    if(r < 0){
        printf("Failed to parse JSON:%d\n",r);
        return -1;
    }

	/* Assume the top-level element is an object */
	if (r < 1 || t[0].type != JSMN_OBJECT) {
		printf("Object expected\n");
		return -1;
	}


    auto dram_obj = std::make_shared<Dram>(1024);
    auto sram_obj = std::make_shared<Sram>(SRAM_SIZE, true);
    auto conv_dma_obj = std::make_shared<ConvDMA>(dram_obj, sram_obj);
    u8 dram_buffer[DRAM_SIZE];
#if 1
    std::uniform_int_distribution<u8> dist(0,255);
    std::default_random_engine generator;
    for(u32 index = 0;index < DRAM_SIZE;index++){
        dram_buffer[index] = dist(generator);
    }
    dram_obj->write_mem(0, dram_buffer, DRAM_SIZE);
    dump_tofile("dram.hex",dram_buffer,NULL,32,256);
#else
    dump_fromfile("dram.hex",dram_buffer,32,256);
#endif
    dram_obj->write_mem(0, dram_buffer, DRAM_SIZE);

    conv_dma_obj->convDmaEn = true;


    /* Loop over all keys of the root object */
    char key[1000];
    int value1,value2;
	for (i = 1; i < r; i++) {
        if (jsoneq(json_buf, &t[i], "Reg") == 0) {
			printf("- Reg Set:\n");
			if (t[i+1].type != JSMN_ARRAY) {
                printf("Reg is not a json_array!\n");
                return -1;
			}
        }
        if (jsoneq(json_buf, &t[i], "name") == 0) {
            strncpy(key,json_buf + t[i+1].start,t[i+1].end - t[i+1].start);
            key[t[i+1].end - t[i+1].start] = '\0';
        }
        if(jsoneq(json_buf, &t[i], "testvalue") == 0)
        {
            if(strcmp(key,"vgrEn") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->vgrEn[0] = value1;
                conv_dma_obj->vgrEn[1] = value2;
                printf("%d : set to vgrEn0\n",value1);
                printf("%d : set to vgrEn1\n",value2);
            }
            if(strcmp(key,"vgrRaddr_l") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->vgrRaddr_l[0] = value1;
                conv_dma_obj->vgrRaddr_l[1] = value2;
                printf("%d : set to vgrRaddr_l0\n",value1);
                printf("%d : set to vgrRaddr_l1\n",value2);
            }
            if(strcmp(key,"vgrRaddr_h") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->vgrRaddr_h[0] = value1;
                conv_dma_obj->vgrRaddr_h[1] = value2;
                printf("%d : set to vgrRaddr_h0\n",value1);
                printf("%d : set to vgrRaddr_h1\n",value2);
            }
            if(strcmp(key,"dramAddrStep") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->dramAddrStep[0] = value1;
                conv_dma_obj->dramAddrStep[1] = value2;
                printf("%d : set to dramAddrStep0\n",value1);
                printf("%d : set to dramAddrStep1\n",value2);
            }
            if(strcmp(key,"burstDataLen") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->burstDataLen[0] = value1;
                conv_dma_obj->burstDataLen[1] = value2;
                printf("%d : set to burstDataLen0\n",value1);
                printf("%d : set to burstDataLen1\n",value2);
            }
            if(strcmp(key,"burstDataTimes") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->burstDataTimes[0] = value1;
                conv_dma_obj->burstDataTimes[1] = value2;
                printf("%d : set to burstDataTimes0\n",value1);
                printf("%d : set to burstDataTimes1\n",value2);
            }
            if(strcmp(key,"writeDataLen") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->writeDataLen[0] = value1;
                conv_dma_obj->writeDataLen[1] = value2;
                printf("%d : set to writeDataLen0\n",value1);
                printf("%d : set to writeDataLen1\n",value2);
            }
            if(strcmp(key,"sramWaddr") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->sramWaddr[0] = value1;
                conv_dma_obj->sramWaddr[1] = value2;
                printf("%d : set to sramWaddr0\n",value1);
                printf("%d : set to sramWaddr1\n",value2);
            }
            if(strcmp(key,"sramWaddr_offset") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->sramWaddr_offset[0] = value1;
                conv_dma_obj->sramWaddr_offset[1] = value2;
                printf("%d : set to sramWaddr_offset0\n",value1);
                printf("%d : set to sramWaddr_offset1\n",value2);
            }
            if(strcmp(key,"sramAddrStep") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->sramAddrStep[0] = value1;
                conv_dma_obj->sramAddrStep[1] = value2;
                printf("%d : set to sramAddrStep0\n",value1);
                printf("%d : set to sramAddrStep1\n",value2);
            }
            if(strcmp(key,"transpose") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->transpose[0] = value1;
                conv_dma_obj->transpose[1] = value2;
                printf("%d : set to transpose0\n",value1);
                printf("%d : set to transpose1\n",value2);
            }
            if(strcmp(key,"padInter") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->padInter[0] = value1;
                conv_dma_obj->padInter[1] = value2;
                printf("%d : set to padInter0\n",value1);
                printf("%d : set to padInter1\n",value2);
            }
            if(strcmp(key,"padZero") == 0){
                value1 = getvalue(json_buf,t[i+2].start + 4,t[i+2].end);
                value2 = getvalue(json_buf,t[i+3].start + 4,t[i+3].end);
                conv_dma_obj->padZero[0] = value1;
                conv_dma_obj->padZero[1] = value2;
                printf("%d : set to padZero0\n",value1);
                printf("%d : set to padZero1\n",value2);
            }
       }
   	}

    conv_dma_obj->Run();
    auto sram_array_pointer = sram_obj->getArrayPointer();
    auto sram_flip_pointer = sram_obj->getFlipNumPointer();
    dump_tofile("sram.hex",(u8*)sram_array_pointer, sram_flip_pointer,16,512);
    fclose(fp);
    delete [] json_buf;
    delete [] t;
    return 0;
}
